package visitor;

import java.util.Iterator;
import compositeExpression.Cond;
import compositeExpression.Expr;
import compositeExpression.Expression;
import compositeExpression.Term;
import compositeExpression.Termb;
import compositeExpression.Termp;
import compositeExpression_Fact.FactExpr;
import compositeExpression_Fact.FactId;
import compositeExpression_Fact.FactNum;
import compositeExpression_Factb.FactbCond;
import compositeExpression_Factb.FactbNot;
import compositeExpression_Factb_FactbExpr.RelopEq;
import compositeExpression_Factb_FactbExpr.RelopGe;
import compositeExpression_Factb_FactbExpr.RelopGt;
import compositeExpression_Factb_FactbExpr.RelopLe;
import compositeExpression_Factb_FactbExpr.RelopLt;
import compositeExpression_Factb_FactbExpr.RelopNeq;
import compositeExpression_Term.MinusTerm;
import compositeExpression_Term.PlusTerm;
import compositeExpression_Termp.DivTermp;
import compositeExpression_Termp.MulTermp;
import compositeExpression_Termp.RemTermp;


public class EvaluationVisitor implements Visitor {
	 private int val;
	 private boolean boolVal;

	    public EvaluationVisitor() {

	        val = 0;
	        boolVal = false;
	    }

	    @Override
	    public void visit(Cond cond) {

	        if (cond.getState().size() == 1) {

	            cond.getState().get(0).accept(this);
	        } else {

	            for (Expression tb : cond.getState()) {

	                tb.accept(this);

	                if (boolVal) {// cond. cortocircuito OR

	                    break;
	                }
	            }
	        }
	    }

	    @Override
	    public void visit(Termb termb) {

	        if (termb.getState().size() == 1) {

	            termb.getState().get(0).accept(this);
	        } else {

	            for (Expression fb : termb.getState()) {

	                fb.accept(this);

	                if (!boolVal) {// cond. cortocircuito AND

	                    break;
	                }
	            }
	        }
	    }

	    @Override
	    public void visit(FactbCond factbCond) {

	        factbCond.getState().accept(this);
	    }

	    @Override
	    public void visit(FactbNot factbNot) {

	        factbNot.getState().accept(this);

	        boolVal = !boolVal;
	    }

	    @Override
	    public void visit(RelopEq relopEq) {

	        int left = 0;
	        int right = 0;

	        relopEq.getleft().accept(this);
	        left = val;

	        relopEq.getRight().accept(this);
	        right = val;

	        if (left == right) {

	            boolVal = true;
	        } else {

	            boolVal = false;
	        }
	    }

	    @Override
	    public void visit(RelopGe relopGe) {

	        int left = 0;
	        int right = 0;

	        relopGe.getleft().accept(this);
	        left = val;

	        relopGe.getRight().accept(this);
	        right = val;

	        if (left >= right) {

	            boolVal = true;
	        } else {

	            boolVal = false;
	        }
	    }

	    @Override
	    public void visit(RelopGt relopGt) {

	        int left = 0;
	        int right = 0;

	        relopGt.getleft().accept(this);
	        left = val;

	        relopGt.getRight().accept(this);
	        right = val;

	        if (left > right) {

	            boolVal = true;
	        } else {

	            boolVal = false;
	        }
	    }

	    @Override
	    public void visit(RelopLe relopLe) {

	        int left = 0;
	        int right = 0;

	        relopLe.getleft().accept(this);
	        left = val;

	        relopLe.getRight().accept(this);
	        right = val;

	        if (left <= right) {

	            boolVal = true;
	        } else {

	            boolVal = false;
	        }
	    }

	    @Override
	    public void visit(RelopLt relopLt) {

	        int left = 0;
	        int right = 0;

	        relopLt.getleft().accept(this);
	        left = val;

	        relopLt.getRight().accept(this);
	        right = val;

	        if (left < right) {

	            boolVal = true;
	        } else {

	            boolVal = false;
	        }
	    }

	    @Override
	    public void visit(RelopNeq relopNeq) {

	        int left = 0;
	        int right = 0;

	        relopNeq.getleft().accept(this);
	        left = val;

	        relopNeq.getRight().accept(this);
	        right = val;

	        if (left != right) {

	            boolVal = true;
	        } else {

	            boolVal = false;
	        }
	    }

	    @Override
	    public void visit(Expr expr) {

	        int temp = 0;

	        if (expr.getState().size() == 1) {

	            expr.getState().get(0).accept(this);
	        } else {

	            for (Expression te : expr.getState()) {

	                te.accept(this);

	                if (te instanceof PlusTerm) {

	                    temp += val;
	                } else if (te instanceof MinusTerm) {

	                    temp -= val;
	                } else {

	                    temp = val;
	                }
	            }
	            val = temp;
	        }
	    }

	    @Override
	    public void visit(Term term) {

	        int temp = 0;

	        if (term.getState().size() == 1) {

	            term.getState().get(0).accept(this);
	        } else {

	            for (Expression tp : term.getState()) {

	                tp.accept(this);

	                if (tp instanceof MulTermp) {

	                    temp *= val;
	                } else if (tp instanceof DivTermp) {

	                    if (val == 0) {

	                        throw new IllegalStateException("Divisione per 0 non consentita");
	                    } else {

	                        temp /= val;
	                    }
	                } else if (tp instanceof RemTermp) {

	                    temp %= val;
	                } else {

	                    if (temp == 0) {

	                        temp = val;
	                    } else {

	                        throw new IllegalStateException();
	                    }
	                }
	            }
	            val = temp;
	        }
	    }

	    @Override
	    public void visit(Termp termp) {

	        int exponent = 0;
	        int base = 0;

	        if (termp.getState().size() == 1) {

	            termp.getState().get(0).accept(this);
	        } else {

	            Iterator<Expression> it = termp.getState().iterator();
	            Expression temp = it.next();

	            temp.accept(this);

	            base = val;

	            while (it.hasNext()) {

	                Expression temp1 = it.next();

	                temp1.accept(this);

	                if (exponent == 0) {

	                    exponent = val;
	                } else {

	                    exponent *= val;
	                }
	            }

	            val = (int) Math.pow(base, exponent);
	        }
	    }

	    @Override
	    public void visit(FactExpr factExpr) {

	        factExpr.getState().accept(this);
	    }

	    @Override
	    public void visit(FactId factId) {

	        val = factId.getState();
	    }

	    @Override
	    public void visit(FactNum factNum) {

	        val = factNum.getState();
	    }

	    public boolean getResult() {

	        return boolVal;
	    }

}
